import { Controller, Get, Post, Param, Body, Query, Inject, UnauthorizedException, DefaultValuePipe, HttpStatus, UseInterceptors, UploadedFile, BadRequestException } from '@nestjs/common';
import { UserService } from './user.service';
import { RegisterUserDto } from "./dto/register-user.dto";
import { LoginUserDto } from "./dto/login-user.dto";
import { UpdateUserPasswordDto } from "./dto/update-user-password.dto";
import { UpdateUserDto } from "./dto/update-user.dto";

import { UserDetailVo } from "./vo/user-info.vo";

import { RedisService } from "../redis/redis.service";
import { EmailService } from "../email/email.service";

import { JwtService } from "@nestjs/jwt";
import { ConfigService } from "@nestjs/config";
import { RequireLogin, UserInfo } from 'src/custom.decorator';

import { generateParseIntPipe } from "../utils";

import { ApiTags, ApiQuery, ApiBody, ApiResponse, ApiBearerAuth } from "@nestjs/swagger";
import { LoginUserVo } from './vo/login-user.vo';
import { RefreshTokenVo } from "./vo/refresh-token.vo";
import { FileInterceptor } from '@nestjs/platform-express';

import * as path from "path";
import { storage } from 'src/my-file-storage';

@ApiTags("用户管理模块")
@Controller('user')
export class UserController {
  constructor(
      private readonly userService: UserService,
      private readonly redisService: RedisService,
      private readonly emailService: EmailService
    ) {}

    @Inject(JwtService)
    private jwtService: JwtService;

    @Inject(ConfigService)
    private configService: ConfigService;


  // 初始化数据接口
  @Get("init-data")
  async initData() {
    await this.userService.initData();
    return "done";
  }  

  // 注册接口
  @ApiBody({type: RegisterUserDto})
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: "验证码已失效/验证码不正确/用户已存在",
    type: String
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: "注册成功/失败",
    type: String
  })
  @Post("register")
  async register(@Body() registerUser: RegisterUserDto) {
    return await this.userService.register(registerUser);
  }

  // 获取验证码接口
  @ApiQuery({
    name: "address",
    type: String,
    required: true,
    example: "xxx@xxx.com"
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: "发送成功",
    type: String
  })
  @Get("register-captcha")
  async captcha(@Query("address") address: string) {
    const code = Math.random().toString().slice(2, 8);

    await this.redisService.set(`captcha_${address}`, code, 5*60);

    await this.emailService.sendMail({
      to: address,
      subject: "注册验证码",
      html: `<p>你的注册验证码是${code}</p>`
    });

    return "发送成功";
  }

  // 登录接口
  @ApiBody({
    type: LoginUserDto
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: "用户不存在/密码错误",
    type: String
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: "用户信息和token",
    type: LoginUserVo
  })
  @Post("login")
  async login(@Body() loginUser: LoginUserDto) {
    console.log(loginUser);
    const vo = await this.userService.login(loginUser, false);
    vo.accessToken = this.jwtService.sign({
      userId: vo.userInfo.id,
      username: vo.userInfo.username,
      email: vo.userInfo.email, // 往token中添加注册邮箱信息，保证登录后，再次进行邮箱验证时是用户注册过的邮箱
      roles: vo.userInfo.permissions,
      permissions: vo.userInfo.permissions
    }, { expiresIn: this.configService.get("jwt_access_token_expires_time") || "30m" });

    vo.refreshToken = this.jwtService.sign({
      userId: vo.userInfo.id
    }, {expiresIn: this.configService.get("jwt_refresh_token_expires_time") || "7d"});

    return vo;
  }

  @ApiBody({
    type: LoginUserDto
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: "用户不存在/密码错误",
    type: String
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: "用户信息和token",
    type: LoginUserVo
  })
  @Post("admin/login")
  async adminLogin(@Body() loginUser: LoginUserDto) {
    console.log(loginUser);
    const vo = await this.userService.login(loginUser, true);
    vo.accessToken = this.jwtService.sign({
      userId: vo.userInfo.id,
      username: vo.userInfo.username,
      email: vo.userInfo.email, // 往token中添加注册邮箱信息，保证登录后，再次进行邮箱验证时是用户注册过的邮箱
      roles: vo.userInfo.permissions,
      permissions: vo.userInfo.permissions
    }, { expiresIn: this.configService.get("jwt_access_token_expires_time") || "30m" });

    vo.refreshToken = this.jwtService.sign({
      userId: vo.userInfo.id
    }, {expiresIn: this.configService.get("jwt_refresh_token_expires_time") || "7d"});
    return vo;
  }

  // 刷新token接口
  @ApiQuery({
    name: "refreshToken",
    type: String,
    description: "刷新token",
    required: true,
    example: "xxxxxxyyyyyzzz"
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: "token失效，请重新登录"
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: "刷新成功",
    type: RefreshTokenVo
  })
  @Get("refresh")
  async refresh(@Query("refreshToken") refreshToken: string) {
    try {
      const data = this.jwtService.verify(refreshToken);

      const user = await this.userService.findUserById(data.userId, false);

      const access_token = this.jwtService.sign({
        userId: user.id,
        username: user.username,
        roles: user.roles,
        email: user.email, // 往token中添加注册邮箱信息，保证登录后，再次进行邮箱验证时是用户注册过的邮箱
        permissions: user.permissions
      }, {
        expiresIn: this.configService.get("jwt_access_token_expires_time") || "30m"
      });

      const refresh_token = this.jwtService.sign({
        userId: user.id
      }, {
        expiresIn: this.configService.get("jwt_refresh_token_expires_time") || "7d"
      });

      const vo = new RefreshTokenVo();
      vo.access_token = access_token;
      vo.refresh_token = refresh_token;

      return vo;
      
    }catch(error) {
      throw new UnauthorizedException("token已失效，请重新登录");
    }
  }
  // 管理用户刷新token接口
  @ApiQuery({
    name: "refreshToken",
    type: String,
    description: "刷新token",
    required: true,
    example: "xxxxxxyyyyyzzz"
  })
  @ApiResponse({
    status: HttpStatus.UNAUTHORIZED,
    description: "token失效，请重新登录"
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: "刷新成功",
    type: RefreshTokenVo
  })
  @Get("admin/refresh")
  async adminRefresh(@Query("refreshToken") refreshToken: string) {
    try {
      const data = this.jwtService.verify(refreshToken);

      const user = await this.userService.findUserById(data.userId, true);

      const access_token = this.jwtService.sign({
        userId: user.id,
        username: user.username,
        email: user.email, // 往token中添加注册邮箱信息，保证登录后，再次进行邮箱验证时是用户注册过的邮箱
        roles: user.roles,
        permissions: user.permissions
      }, {
        expiresIn: this.configService.get("jwt_access_token_expires_time") || "30m"
      });

      const refresh_token = this.jwtService.sign({
        userId: user.id
      }, {
        expiresIn: this.configService.get("jwt_refresh_token_expires_time") || "7d"
      });

      return {
        access_token,
        refresh_token
      }
      
    }catch(error) {
      throw new UnauthorizedException("token已失效，请重新登录");
    }
  }

  // 查询用户信息--用来做用户信息回显
  @ApiBearerAuth() // 该接口需要登录状态，给swagger文档增加token校验（main.ts中也需要加一下）
  @ApiResponse({
    status: HttpStatus.OK,
    description: "success",
    type: UserDetailVo
  })
  @Get("info")
  @RequireLogin()
  async info(@UserInfo("userId") userId: number) {
    const user = await this.userService.findUserDetailById(userId);
    // 不要将所有用户信息返回，因为密码等设计安全的信息要去掉，所以我们封装一个VO，指定要放回哪些用户信息
    const vo = new UserDetailVo();
    vo.id = user.id;
    vo.username = user.username;
    vo.email = user.email;
    vo.headPic = user.headPic;
    vo.phoneNumber = user.phoneNumber;
    vo.nickName = user.nickName;
    vo.createTime = user.createTime;
    vo.isFrozen = user.isFrozen;

    return vo;
  }

  // 修改用户密码接口--普通用户和管理员用户逻辑一样，我们通过给Post装饰器传递数组实现一块写
  @ApiBody({
    type: UpdateUserPasswordDto
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: "验证码失效/不正确",
    type: String
  })
  @Post(["update_password", "admin/update_password"])
  async updatePassword(@Body() passwordDto: UpdateUserPasswordDto ) {
    console.log(passwordDto);
    return await this.userService.updatePassword(passwordDto);
  }

  // 修改密码时发送验证码接口
  @ApiQuery({
    name: "address",
    required: true,
    description: "邮箱地址",
    type: String
  })
  @ApiResponse({
    type: String,
    status: HttpStatus.OK,
    description: "发送成功"
  })
  @Get("update_password/captcha")
  async updatePasswordCaptcha(@Query("address") address: string) {
    const code = Math.random().toString().slice(2, 8);

    await this.redisService.set(`update_password_captcha_${address}`, code, 10*60);

    await this.emailService.sendMail({
      to: address,
      subject: "更新密码验证码",
      html: `<p>你的更改密码验证码是 ${code}</p>`
    });

    return "发送成功";
  }

  // 更新用户信息
  @ApiBearerAuth()
  @ApiBody({
    type: UpdateUserDto
  })
  @ApiResponse({
    status: HttpStatus.BAD_REQUEST,
    description: "验证码失效/不正确",
    type: String
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: "更新成功",
    type: String
  })
  @Post(["update", "admin/update"])
  @RequireLogin()
  async update(@UserInfo("userId") userId: number, @Body() updateUserDto: UpdateUserDto) {
    return await this.userService.update(userId, updateUserDto);
  }

  // 更新用户信息验证码
  @ApiBearerAuth()
 /*  @ApiQuery({
    name: "address",
    required: true,
    description: "邮箱地址",
    type: String
  }) */ // 不在需要邮箱参数，可以从token中获取，这样可以保证是用户注册过的邮箱
  @ApiResponse({
    type: String,
    status: HttpStatus.OK,
    description: "发送成功"
  })
  @Get("update/captcha")
  async updateCaptcha(@UserInfo('email') address: string) {
    const code = Math.random().toString().slice(2, 8);

    await this.redisService.set(`update_user_captcha_${address}`, code, 10*60);

    await this.emailService.sendMail({
      to: address,
      subject: "更新用户信息验证码",
      html: `<p>你的验证码是${code}</p>`
    });
    
    return "发送成功";
  }

  // 冻结用户
  @ApiBearerAuth()
  @ApiQuery({
    name: "userId",
    description: "用户id",
    type: Number
  })
  @ApiResponse({
    status: HttpStatus.OK,
    type: String,
    description: "success"
  })
  @Get("frozen")
  @RequireLogin()
  async frozen(@Query("id") userId: number ) {
    await this.userService.frozen(userId);
    return "success";
  }
  
  /* 获取用户列表接口
        1. 支持分页查询
        2. 页码格式不对时，要进行错误提示；--这里不会的可以看pipe那一节（由于后面接口可能多处都要用到这个校验，我们抽离出来封装到utils中）
        3. 页码没有传时，要有一个默认值--这里不会的可以看pipe那一节
        4. 用户名，邮件等模糊查询 */
  @ApiBearerAuth()
  @ApiQuery({
    name: "pageNo",
    description: "第几页",
    type: Number
  })
  @ApiQuery({
    name: "pageSize",
    description: "每页多少条",
    type: Number
  })
  @ApiQuery({
    name: "nickName",
    description: "昵称",
    type: Number
  })
  @ApiQuery({
    name: "username",
    description: "用户名",
    type: Number
  })
  @ApiQuery({
    name: "email",
    description: "邮箱地址",
    type: Number
  })
  @ApiResponse({
    type: String,
    description: "用户列表"
  })      
  @Get("list")
  @RequireLogin()
  async list(
    @Query("pageNo", new DefaultValuePipe(1), generateParseIntPipe("pageNo")) pageNo: number,
    @Query("pageSize", new DefaultValuePipe(2), generateParseIntPipe("pageSize")) pageSize: number,
    @Query("nickName") nickName: string,
    @Query("username") username: string,
    @Query("email") email: string
  ) {
    return await this.userService.findUsersByPage(pageNo, pageSize, nickName, username, email);
  }

  /* 文件上传功能--用于用户头像等的文件上传 */
  @Post("upload")
  @UseInterceptors(FileInterceptor('file', { // 含义：提取参数为formData类型中的file参数，并将该文件放到uploads文件夹下
    dest: "uploads",
    storage: storage,
    limits: {
      fileSize: 1024 * 1024 * 3 // 限制最大3M
    },
    fileFilter(req, file, callback) { // 限制只能上传类型和大小
      const extname = path.extname(file.originalname);
      if([".png", ".jpg", ".gif"].includes(extname)) {
        callback(null, true);
      }else {
        callback(new BadRequestException("只能上传图片"), false);
      }
    }
  }))
  uploadFile(@UploadedFile() file: Express.Multer.File) {
    console.log("file", file);

    return file.path;
  }
}
